package com.shumm.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;


/**
 * @author shumm
 * @Description
 * @since 2023-12-12 15:30
 */
@Component
@Aspect
@Slf4j
public class LogAspect {

    /**
     * 任务1
     */
    @Pointcut("execution(* com.shumm.service.impl.*.*(..))")
    public void myServiceMethods() {
    }

    @AfterReturning(pointcut = "execution(* com.shumm.service.impl.*.select*(..))", returning = "result")
    public void logMethodReturnValue(JoinPoint joinPoint, Object result) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //当前方法的调用时间
        long startTime = System.currentTimeMillis();
        //将startTime转为YYYY-MM-DD HH:mm:ss格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String startTimeStr = sdf.format(startTime);
        //获取当前所在类
        String str = joinPoint.getThis().toString();
        log.info("{}中{}方法开始执行，当前方法的调用时间为：{},返回值为{}", str.substring(str.lastIndexOf(".") + 1, str.indexOf("@")), methodSignature.getName(), startTimeStr, result);
    }

    /**
     * 任务2
     */
    @AfterThrowing(value = "execution(* com.shumm.service.impl.*.add*(..))||" +
            "execution(* com.shumm.service.impl.*.delete*(..))" +
            "||execution(* com.shumm.service.impl.*.update*(..))",throwing="ex")
    public Object throwAdvice(JoinPoint point,Exception ex) {
        Object target = point.getTarget();// 实体类
        String name = point.getSignature().getName();//方法名称
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Object object = null;
        String t = "";
        String exs = "";
        String cause = "";
        log.error("============================================================>");
        t = simpleDateFormat.format(System.currentTimeMillis()); //获取当前时间
        exs = ex.getMessage();
        log.error("{}.{}() 异常发生时间为 {}\n, 报错内容为{}\n", target, name, t, exs);
        log.error("============================================================>");
        return object;
    }


    /**
     * 任务3
     */
    //在service类中的方法执行后无论是成功执行还是出现异常，都将以下信息输出到控制台：方法调用的时间，方法的方法名和所在类名。
    @After(value = "execution(* com.shumm.service.impl.*.*(..))")
    public void logAfterServiceMethod(JoinPoint point) {
        String name = point.getSignature().getName();
        long startTime = System.currentTimeMillis();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String startTimeStr = sdf.format(startTime);
        String classType = point.getSignature().getClass().getName();
        log.info("{}中{}方法结束执行，方法调用时间为{}", classType, name, startTimeStr);
    }

    /**
     * 任务4
     */


    @Before("execution(* com.shumm.service.impl.*.*(..))")
    public void logBeforeMethodExecution(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //当前方法的调用时间
        long startTime = System.currentTimeMillis();
        //将startTime转为YYYY-MM-DD HH:mm:ss格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String startTimeStr = sdf.format(startTime);
        //获取当前方法参数
        Object[] args = joinPoint.getArgs();
        //获取当前所在类
        String str = joinPoint.getThis().toString();
        log.info("{}中{}方法开始执行，方法参数为{}，调用时间为{}", str.substring(str.lastIndexOf(".") + 1, str.indexOf("@")), methodSignature.getName(), args, startTimeStr);
    }

    /**
     * 任务5 记录service类的查询方法执行消耗的时间，将一下信息输出到控制台，方法的方法名，所在类名，开始执行的时间点，方法执行完成的时间点，方法执行消耗的时间(ms)
     */
    //记录service类的查询方法执行消耗的时间，将一下信息输出到控制台，方法的方法名，所在类名，开始执行的时间点，方法执行完成的时间点，方法执行消耗的时间(ms)
    @Around("myServiceMethods()")
    public Object logMethodRunningTime(ProceedingJoinPoint point)
            throws Throwable {

        /*
          ProceedingJoinPoint 继承了 JoinPoint
          ProceedingJoinPoint 为特殊的JoinPoint，可以调用proceed()方法继续执行目标方法, 即可达到前置 + 后置 通知的效果
         */
        Object target = point.getTarget();// 实体类
        String name = point.getSignature().getName();//方法名称

        long start = System.currentTimeMillis(); // 开始时间
        Object object = point.proceed(); //
        long end = System.currentTimeMillis(); // 结束时间
        long t = end - start;//差值
        log.info("{}.{}() 运行时间为 {} ms", target, name, t);
        return object;
    }
}
